<?php

require_once 'db_base.inc';
require_once 'string.inc';



/* for MYSQL

'SET NAMES cp1251'

SET NAMES ... 
	- indicates what character set the client will use to send SQL statements to the server.
	- specifies the character set that the server should use for sending results back to the client
equal to
	SET character_set_client = ...;
	SET character_set_results = ...;
	SET character_set_connection = ...;
	
SET CHARACTER SET is similar to SET NAMES but sets character_set_connection and collation_connection to character_set_database and collation_database

http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html
*/


class Database 
	implements iDatabase
{
   function __construct($conn, $user, $pass, $charset)
   {
      $this->dbh = new PDO($conn, $user, $pass);
      $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
      $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);


		if ($this->dbh->getAttribute(PDO::ATTR_DRIVER_NAME) != 'mysql') {
			throw Exception("This PDO library works only with MYSQL driver");
		}
		// MYSQL specific
		$this->dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, 1);
		$this->dbh->exec("SET NAMES $charset");
	}

   function __destruct() 
   {
      $this->dbh = null;
   }
   
   public function lastInsertId($seqName=null) 
   {
		return $this->dbh->lastInsertId($seqName);
   }
   
	public function quote($s)
	{
		return $this->dbh->quote($s);
	}
	
   public $dbh = null;
};




class Query 
	implements iQuery, Iterator
{
   function __construct(Database $db) 
   {
      $this->db = $db;
   }

   function __destruct() 
   {
      $this->destroy();
   }

	/// don't use '' around and don't quote() string placeholders
   public function prepare($sql) 
   {
      $this->destroy();
      $this->sth = $this->db->dbh->prepare($sql);
   }

   public function setParam($nm, $val) 
   {
      assert(isset($this->sth));  //no use to set param before calling prepare
      $snm = new String($nm);
      $this->params[$snm->prepend(':')] = $val;
      return $this;
   }

   public function clearParams() 
   {
      $this->params = array();
   }

   public function execute()
   {
      assert(isset($this->sth) && !$this->simple);
      $this->sth->closeCursor();
      if (false === $this->sth->execute($this->params)) {
      	if ($this->sth->errorCode() !== '00000') {
				throw new Exception(
	         	"PDOStatement execute failure [" .
	         		$this->sth->errorCode() .
	         	']'
				);
	      }
      }
   }
   
   public function exec($sql) 
   {
   	try {
			$this->destroy();
			$this->simple = true;
			$this->sql = $sql;
			$this->sth = $this->db->dbh->query($sql);
		} 
		catch(Exception $e) {
			echo $sql;
			throw $e;
		}
   }
   
	public function processedCount()
	{
		assert(isset($this->sth));
		
		return $this->sth->rowCount();
	}
   
	///false on error
   public function fetch($type=DB_FETCH_NUM)
   {
      assert(isset($this->sth));
      
      if ($type==DB_FETCH_ASSOC) {
      	$type = PDO::FETCH_ASSOC;
      }
      else {
      	$type = PDO::FETCH_NUM;
      }
      
      $this->result = $this->sth->fetch($type);
      return $this->result;
   }


   public function restart() 
   {
   	if ($this->simple) {
   		$this->exec($this->sql);
   	}
   	else {
      	assert(isset($this->sth));
      	$this->execute();
      }
   }

   public function eof() 
   {
      assert(isset($this->sth));
      return false === $this->result;
   }

   public function at($fnm) 
   {
      assert(isset($this->sth) && false !== $this->result);  
                                              //didn't call execute?
      if (!array_key_exists($fnm,$this->result)) {
         throw new Exception(
            "Result column [$fnm] not found among [" . 
            implode(',',array_keys($this->result)) . "]"
         );
      }
      return $this->result[$fnm];
   }

   public function close()
   {
      $this->clearParams();
      $this->result = false;
      if (isset($this->sth)) {
         $this->sth->closeCursor();
      }
   }


   public function rewind() 
   {
      reset($this->result);
   }

   public function current() 
   {
      return current($this->result);
   }

   public function key() 
   {
      return key($this->result);
   }

   public function next() 
   {
      return next($this->result);
   }

   public function valid() 
   {
      return $this->current() !== false;
   }

   public function __toString() 
   {
      $s = '';
      foreach ($this->result as $key => $val) {
         $s .= "[$key] = $val\n";
      }
      return $s;
   }

   public function __clone() 
   {
      assert(false);
   }



   private function destroy() 
   {
      $this->close();
      unset($this->sth);
		$this->simple = false;
   }


   protected $db = null;      //$db->dbh - PDO object
   private $sth = null;       //PDOStatement object
   private $params = array();
   private $result = false;
   private $simple = false;
   private $sql = '';
};

?>